﻿using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Linq;

using DST = System.Diagnostics.DebuggerStepThroughAttribute;

namespace System
{
    /// <summary>Класс методов расширений для обработчиков событий</summary>
    public static class EventHandlerExtension
    {
        /// <summary>Потоко-безопасная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <param name="e">Аргумент события</param>
        [DST]
        public static void Start(this EventHandler Handler, object Sender, EventArgs e)
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return;
            var invokations = lv_Handler.GetInvocationList();
            for(var i = 0; i < invokations.Length; i++)
            {
                var lv_T = invokations[i];
                if(lv_T.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)lv_T.Target).InvokeRequired)
                    ((ISynchronizeInvoke)lv_T.Target).Invoke(lv_T, new[] { Sender, e });
                else
                    lv_T.DynamicInvoke(Sender, e);
            }
        }

        /// <summary>Потоко-безопасная асинхронная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <param name="e">Аргумент события</param>
        /// <param name="CallBack">Метод завершения генерации события</param>
        /// <param name="State">Объект-состояние, Передаваемый в метод завершения генерации события</param>
        [DST]
        public static IAsyncResult StartAsync(this EventHandler Handler, object Sender, EventArgs e,
                                      AsyncCallback CallBack = null, object @State = null)
        {
            var lv_Handler = Handler;
            Action action = () => { if(lv_Handler != null) lv_Handler(Sender, e); };
            return action.BeginInvoke(CallBack, State);
        }

        /// <summary>Быстрая генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        [DST]
        public static void FastStart(this EventHandler Handler, object Sender) { Handler.FastStart(Sender, EventArgs.Empty); }

        /// <summary>Быстрая генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <param name="e">Аргументы события</param>
        [DST]
        public static void FastStart(this EventHandler Handler, object Sender, EventArgs e) { var lv_Handler = Handler; if(lv_Handler != null) lv_Handler(Sender, e); }

        /// <summary>Быстрая генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TEventArgs">Тип аргумента события</typeparam>
        /// <param name="e">Аргументы события</param>
        [DST]
        public static void FastStart<TEventArgs>(this EventHandler<TEventArgs> Handler, object Sender, TEventArgs e)
                    where TEventArgs : EventArgs
        {
            var lv_Handler = Handler;
            if(lv_Handler != null)
                lv_Handler(Sender, e);
        }

        /// <summary>Потоко-безопасная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TEventArgs">Тип аргумента события</typeparam>
        /// <param name="e">Аргументы события</param>
        [DST]
        public static void Start<TEventArgs>(this EventHandler<TEventArgs> Handler, object Sender, TEventArgs e)
                    where TEventArgs : EventArgs
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return;
            var invokations = lv_Handler.GetInvocationList();
            for(var i = 0; i < invokations.Length; i++)
            {
                var I = invokations[i];
                if(I.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)I.Target).InvokeRequired)
                    ((ISynchronizeInvoke)I.Target).Invoke(I, new[] { Sender, e });
                else
                    I.DynamicInvoke(Sender, e);
            }
        }

        /// <summary>Потоко-безопасная асинхроная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TEventArgs">Тип аргумента события</typeparam>
        /// <param name="e">Аргументы события</param>
        /// <param name="CallBack">Метод завершения генерации события</param>
        /// <param name="State">Объект-состояние, Передаваемый в метод завершения генерации события</param>
        [DST]
        public static IAsyncResult StartAsync<TEventArgs>(this EventHandler<TEventArgs> Handler,
                                                  object Sender, TEventArgs e, AsyncCallback CallBack = null,
                                                  object @State = null)
                    where TEventArgs : EventArgs
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return null;
            return ((Action)(() =>
                          {
                              var lv_InvocationList = lv_Handler.GetInvocationList();
                              foreach(var action in lv_InvocationList)
                                  action.DynamicInvoke(Sender, e);
                          })).BeginInvoke(CallBack, State);
        }

        /// <summary>Потоко-безопасная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TArgs">Тип аргумента события</typeparam>
        /// <param name="Args">Аргументы события</param>
        /// <typeparam name="TResult">Тип результата обработки события</typeparam>
        /// <typeparam name="TSender">Тип источника события</typeparam>
        /// <returns>Массив результатов обработки события</returns>
        [DST]
        public static TResult[] Start<TResult, TSender, TArgs>(this EventHandler<TResult, TSender, TArgs> Handler,
                                                               TSender Sender, TArgs Args)
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return new TResult[0];
            Contract.Assume(lv_Handler != null);
            return lv_Handler
                .GetInvocationList()
                .Select(I => (TResult)(I.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)I.Target).InvokeRequired
                    ? ((ISynchronizeInvoke)I.Target).Invoke(I, new object[] { Sender, Args })
                    : I.DynamicInvoke(Sender, Args))).ToArray();
        }
    }
}
